package com.blue.base.oauth.thirdpart.config.exception;

import com.blue.base.common.bean.resp.JsonResult;
import com.blue.base.common.exception.BaseException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceAware;
import org.springframework.context.NoSuchMessageException;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

/**
 * 全局异常处理器
 *
 * @author liulei
 * @version 1.0
 */
@Order(Ordered.LOWEST_PRECEDENCE - 1)
@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler implements MessageSourceAware {

    private Logger log = LogManager.getLogger(this.getClass());

    private MessageSource messageSource;

    /**
     * 一般异常处理
     */
    @ExceptionHandler(Exception.class)
    public JsonResult defaultErrorHandler(HttpServletRequest request, Exception e, Locale locale) {
        JsonResult result = new JsonResult();
        int code = 500;
        String errorMessage = e.getMessage();
        Throwable exception = e;
        if (exception instanceof BaseException) {
            code = ((BaseException) exception).getCode();
            errorMessage = ((BaseException) exception).getErrorMessage();
        } else {
            log.error("未知异常，错误请求URI = " + request.getRequestURI(), e);
        }
        // 如果异常里已经包含了错误信息，则不会再通过错误码获取预先定义的错误信息
        if (StringUtils.isEmpty(errorMessage)) {
            String prefix = exception.getClass().getSimpleName();
            errorMessage = getMessage(prefix + "." + code, locale);
            if (errorMessage == null) {
                errorMessage = getMessage(Integer.toString(code), locale);
            }
        }
        result.setStatus(code);
        result.setMessage(errorMessage);
        log.error("请求URI = {}, 错误码 = {}, 错误信息 = {}", request.getRequestURI(), code, errorMessage);
        return result;
    }

    private String getMessage(String key, Locale locale) {
        String errorMessage = null;
        try {
            errorMessage = messageSource.getMessage(key, null, locale);
        } catch (NoSuchMessageException exception) {
            log.error("ErrorMessage|NotFound|Custom|Exception|Msg|{}", key);
        }
        return errorMessage;
    }

    /**
     * JSR303验证规范的@Valid实体类注解校验异常处理
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public JsonResult argumentDetailValidate(MethodArgumentNotValidException ex) {
        BindingResult bindingResult = ex.getBindingResult();
        Map<String, String> errorsMap = getErrorsMap(bindingResult);
        String errorMsg = String.join(";", errorsMap.values());
        JsonResult jsonResult = new JsonResult();
        jsonResult.setStatus(BaseException.PARAM_MISSED);
        jsonResult.setMessage(errorMsg);
        return jsonResult;
    }

    /**
     * 不支持的方法类型[前端调用方法类型get/post与后台不匹配]
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public JsonResult notSuchTypeRequestMethod(HttpRequestMethodNotSupportedException ex) {
        JsonResult jsonResult = new JsonResult();
        jsonResult.setStatus(BaseException.METHOD_NOT_SUPPORT);
        jsonResult.setMessage(ex.getMessage());
        return jsonResult;
    }

    private Map<String, String> getErrorsMap(BindingResult result) {
        List<FieldError> fieldErrors = result.getFieldErrors();

        Map<String, String> errorsMap = new HashMap<>(10);
        for (FieldError fieldError : fieldErrors) {
            String localizedErrorMessage = fieldError.getDefaultMessage();
            if (!StringUtils.isEmpty(localizedErrorMessage)) {
                errorsMap.put(fieldError.getField(), localizedErrorMessage);
            }
        }
        return errorsMap;
    }

    @Override
    public void setMessageSource(MessageSource messageSource) {
        this.messageSource = messageSource;
    }
}
